package me.bcloud.web.util;
/*
 * Property of Liberty Mutual Insurance Group
 * Copyright 2003 ALL RIGHTS RESERVED
 */


import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
/**
 * A JCE based utility that encrypts and decrypts strings using the DES
 * algorithm with symetric keys. The constructor initializes a default DES
 * SecretKey and Cipher for the <code>desEncrypt</code> and <code>desDecrypt</code>
 * methods. You can reset them using the <code>setKey</code> and <code>setCipher</code>
 * methods.
 * <p>
 * @author  Ernie Varitimos
 * @version 1.00, 06/27/2002
 * @see EncryptMap
 */
public final class DES
{
	// default secret password used in
	// the password-based encryption.
	private static char[] defaultSecret = {
		'M','#','y','-','S','&','C','r','E','@','t','P','4','%','S','s',
		'W','0','*','r','D','1','2','6','0','0','n','r','!','e','^','b',
		'n','(','u','2','M','#','y','-','0','g','n','j','!','e','^','b',
		'#','x','r','l','S','&','h','r','k','t','P','4','%','S','s','Q'
	};

	// Salt used in the password-based encryption.
	private static byte[] defaultSalt = {
		(byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
		(byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
	};

	// count used in the password-based encryption.
	private static int defaultCount = 20;

	// The generated secret key
	private static SecretKey desKey;

	// The Cipher that emerges from the secret key
	private static Cipher desCipher;

	/** No args constructor. */
	public DES()
	{
		desKey = generateDesKey();
		desCipher = generateDesCipher();
	}

	/**
	  * <p>Supply a "clear text" string to encrypt.
	  * @param string the String to encrypt.
	  * @return the encrypted string as a byte array.
	  */
	public byte[] desEncrypt (String string)
	{
		return desEncrypt(string.getBytes());
	}

	/**
	  * <p>Supply a "clear text" string to encrypt.
	  * @param bytes the String, as a byte array, to encrypt.
	  * @return the encrypted string as a byte array.
	  */
	public byte[] desEncrypt (byte[] bytes)
	{
		return desEncrypt(bytes, getKey(), getCipher());
	}

	/**
	  * <p>Supply a "clear text" string to encrypt.
	  * @param bytes the String, as a byte array, to decrypt.
	  * @param secretKey the SecretKey.
	  * @param cipher the Cipher.
	  * @return the encrypted string as a byte array.
	  */
	public byte[] desEncrypt (byte[] bytes, SecretKey secretKey, Cipher cipher)
	{
		byte[] cleartext = new byte[bytes.length];

		try
		{
			// Initialize the cipher for encryption
			desCipher.init(Cipher.ENCRYPT_MODE, secretKey);

			// Encrypt the cleartext
			cleartext = cipher.doFinal(bytes);

		} catch (Exception e) {
			e.printStackTrace();
		}

		return cleartext;
	}

	/**
	  * <p>Supply an encrypted byte array to decrypt.
	  * @param bytes the String, as a byte array, to decrypt.
	  * @return the decrypted string as a byte array.
	  */
	public byte[] desDecrypt (byte[] bytes)
	{
		return desDecrypt(bytes, getKey(), getCipher());
	}

	/**
	  * <p>Supply an encrypted byte array to decrypt.
	  * @param bytes the String, as a byte array, to decrypt.
	  * @param secretKey the SecretKey.
	  * @param cipher the Cipher.
	  * @return the decrypted string as a byte array.
	  */
	public byte[] desDecrypt (byte[] bytes, SecretKey secretKey, Cipher cipher)
	{
		byte[] ciphertext = new byte[bytes.length];

		try
		{
			// Initialize the same cipher for decryption
			desCipher.init(Cipher.DECRYPT_MODE, secretKey);

			// Decrypt the ciphertext
			ciphertext = cipher.doFinal(bytes);

		} catch (Exception e) {
			e.printStackTrace();
		}

		return ciphertext;
	}

	/**
	  * <p>Supply a "clear text" string to encrypt,
	  * using the default secret password.
	  * <p>
	  * @param string the String to encrypt.
	  * @return the encrypted string as a byte array.
	  */
	public byte[] pbEncrypt (String string)
	{
		return pbEncrypt (string, defaultSecret);
	}


	/**
	  * <p>Supply a "clear text" string to encrypt and
	  * a char array that holds a secret password.
	  * <p>
	  * @param string the String to encrypt.
	  * @param secret the secret password.
	  * @return the encrypted string as a byte array.
	  */
	public byte[] pbEncrypt (String string, char[] secret)
	{
		return pbEncrypt (string.getBytes(), secret, defaultSalt, defaultCount);
	}

	/**
	  * <p>Supply a password and an encrypted byte array to decrypt.
	  * <p>
	  * @param bytes the String, as a byte array, to decrypt.
	  * @param secret the secret password.
	  * @return the encrypted string as a byte array.
	  */
	public byte[] pbDecrypt (byte[] bytes, char[] secret)
	{
		return pbEncrypt (bytes, secret, defaultSalt, defaultCount);
	}

	/**
	  * <p>Supply an encrypted byte array to decrypt,
	  * using the default secret password.
	  * <p>
	  * @param bytes the String, as a byte array, to decrypt.
	  * @return the encrypted string as a byte array.
	  */
	public byte[] pbDecrypt (byte[] bytes)
	{
		return pbEncrypt (bytes, defaultSecret, defaultSalt, defaultCount);
	}

	/**
	  * <p>Supply a password and a "clear text" string to encrypt.
	  * @param bytes the string, as a byte array, to encrypt.
	  * @param secret the secret password.
	  * @param salt the salt seed.
	  * @param count the count seed.
	  * @return the encrypted string as a byte array.
	  */
	public byte[] pbEncrypt (byte[] bytes, char[] secret, byte[] salt, int count)
	{
		// generate the SecretKey 
		SecretKey secretKey = generatePBKey(secret, salt, count);

		try
		{
			// Create PBE parameter set
			PBEParameterSpec paramSpec = new PBEParameterSpec(salt, count);

			// Create PBE Cipher that employs the DES algorithm
			Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");

			// Initialize PBE Cipher with mode, key and parameters
			pbeCipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);

			// return the encrypted the string as byte[]
			return pbeCipher.doFinal(bytes);

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	  * <p>Supply a password and an encrypted byte array to decrypt.
	  * @param bytes the string, as a byte array, to decrypt.
	  * @param secret the secret password.
	  * @param salt the salt seed.
	  * @param count the count seed.
	  * @return the decrypted string as a byte array.
	  */
	public byte[] pbDecrypt (byte[] bytes, char[] secret, byte[] salt, int count)
	{
		// generate the SecretKey 
		SecretKey secretKey = generatePBKey(secret, salt, count);

		try
		{
			// Create PBE parameters
			PBEParameterSpec paramSpec = new PBEParameterSpec(salt, count);

			// Create PBE Cipher
			Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");

			// Initialize PBE Cipher with mode, key and parameters
			pbeCipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec);

			// return the decrypted string as byte[]
			return pbeCipher.doFinal(bytes);

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	  * <p>Returns the password-based DES key.
	  * @param secret the secret password.
	  * @param salt the salt seed.
	  * @param count the count seed.
	  * @return the des SecretKey.
	  */
	public SecretKey generatePBKey(char[] secret, byte[] salt, int count)
	{
		try
		{
			// convert it to a SecretKey object, using the secret password.
			PBEKeySpec keySpec = new PBEKeySpec(secret);
			SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");

			return keyFac.generateSecret(keySpec);

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	  * <p>Generates a DES key.
	  * @return the DES SecretKey.
	  */
	private SecretKey generateDesKey()
	{
		try
		{
			KeyGenerator keygen = KeyGenerator.getInstance("DES");
			return keygen.generateKey();

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	  * <p>Generates a DES Cipher.
	  * @return the DES Cipher.
	  */
	private Cipher generateDesCipher()
	{
		try
		{
			return Cipher.getInstance("DES");

		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	  * <p>Returns the DES SecretKey.
	  * @return the DES SecretKey.
	  */
	public SecretKey getKey() { return desKey; }

	/** <p>Sets the DES key.
	  * @param secretKey the SecretKey.
	  */
	public void setKey(SecretKey secretKey) { desKey = secretKey; }

	/**
	  * <p>Returns the DES Cipher.
	  * @return the DES Cipher.
	  */
	public Cipher getCipher() { return desCipher; }


	/** <p>Sets the DES Cipher.
	  * @param cipher the Cipher.
	  */
	public void setCipher(Cipher cipher) { desCipher = cipher; }


	public static void main(String[] args)
	{


		DES crypt = new DES();

		// supply the string to encrypt
		byte[] encrypted = crypt.desEncrypt("bobo".getBytes());

		System.out.println("Encrypted: " + new String(encrypted));
		System.out.println("Decrypted: " + new String(crypt.desDecrypt(encrypted)));
	}

}